﻿package com.swfmessenger.net
{

/*****************************************************************************

	Class: URLLoaderEx
	Author: Marcos Augusto Bitetti
	função: URLLoaderEx faz chamadas HTTP com a capacidade de retornar TODAS
			as informações retornadas pelo servidor.
	
	Detalhes:	Esta classe foi concebida graças às pesquizas de um outro programador,
				o q me poupou um tempo incrível, podendo me dedicar apenas ao que este
				projeto se destina. Tive que no máximo implementar coisas que n foram
				implementas na classe dele. Tirar umas excedentes e acrescentar umas
				especificas.
				Aplausos ao nosso amigo indiano Abdul Qabiz :
				http://www.abdulqabiz.com/blog/archives/flash_and_actionscript/http_authentica.php
				

/****************************************************************************/

import flash.net.Socket;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import flash.utils.Dictionary;
import flash.events.EventDispatcher;
import flash.events.SecurityErrorEvent;
import flash.events.HTTPStatusEvent;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.Event;
import flash.system.Security;
import flash.errors.EOFError;

import com.swfmessenger.utils.StringEx;

public dynamic class URLLoaderEx extends EventDispatcher
{
	//Events
	[Event(name="close", type="flash.events.Event.CLOSE")]
	[Event(name="complete", type="flash.events.Event.COMPLETE")]
	[Event(name="open", type="flash.events.Event.CONNECT")]
	[Event(name="ioError", type="flash.events.IOErrorEvent.IO_ERROR")]
	[Event(name="securityError", type="flash.events.SecurityErrorEvent.SECURITY_ERROR")]
	[Event(name="progress", type="flash.events.ProgressEvent.PROGRESS")]
	[Event(name="httpStatus", type="flash.events.HTTPStatusEvent.HTTP_STATUS")]

	//class attributes
	private var socket:Socket;
	private var request:URLRequest;
	//public  var httpRequest:String;
	//public  var httpServer:String;
	private var headerComplete:Boolean = false;
	private var header:Dictionary;
	public var data:String = "";
	private var bytesLoaded:int = 0;
	private var bytesTotal:int = 0;
	private var headerTmp:String = "";
	private var httpVersion:Number;
	private var httpStatusCode:int;
	private var httpStatusText:String;
	
	public var httpPort:uint = 80;
	public var httpHeadersString:String;
	
	public function URLLoaderEx(request:URLRequest = null) {
		// create http-socket
		socket = new Socket();

		//subscribe to events
		socket.addEventListener( Event.CONNECT , onConnectEvent , false , 0 );				
		socket.addEventListener( Event.CLOSE , onCloseEvent , false, 0 );
		socket.addEventListener( IOErrorEvent.IO_ERROR , onIOErrorEvent , false, 0 );
		socket.addEventListener( SecurityErrorEvent.SECURITY_ERROR , onSecurityErrorEvent , false, 0 );
		socket.addEventListener( ProgressEvent.SOCKET_DATA , onSocketDataEvent , false , 0 );
		
		if(request) load(request);
	}
	
	public function load(request:URLRequest):void {
		if (!request) return;
		
		this.request = request;
		//
		
		var httpServer:String = getServerName(request.url);
		if (httpServer!=null) {
			try {
				headerTmp = "";
				data = "";
				headerComplete = false;
				bytesTotal = bytesLoaded = 0;
				header = null;
				socket.connect(httpServer, httpPort);
			} catch (e:Error) {
				throw new Error("Invalid URL: " + httpServer);
			}
		}
	}
	
	public function close():void {
		socket.close();
	}
	
	private function getServerName(url:String):String {
		var d:Array = url.split('http://')[1].split('/');
		if(d.length > 0) {
			var host:String = d[0];//d.shift();
			/*
			//este try de segurança eu desabilitei, pq o server do msn n tem o crossdomain.xml "FODA-SE"
			//load crossdomain, if its on server.
			try {
				Security.loadPolicyFile("http://"+host+"/crossdomain.xml");
			} catch(e:Object) {
				
			}
			*/
			return d.join('/');
		} else return null;		
	}
	
	private function onConnectEvent(event:Event):void {	
		sendHeaders();
		dispatchEvent(new Event(Event.CONNECT));
	}
		
	private function onCloseEvent(event:Event):void {
		dispatchEvent(new Event(flash.events.Event.COMPLETE));
	}
		
	private function onIOErrorEvent(event:IOErrorEvent):void { 
		dispatchEvent(event);
	}
	
	private function onSecurityErrorEvent(event:SecurityErrorEvent):void {
		dispatchEvent(event.clone());
	}

	private function onSocketDataEvent(event:ProgressEvent):void {
		//pega dados recebidos e envia a informação do evento onProgress
		bytesLoaded += socket.bytesAvailable;
		dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, bytesLoaded, bytesTotal));
		
		var str:String = "";
		try 
		{
			str = socket.readUTFBytes(socket.bytesAvailable);
		}  catch(e:Error) {
			throw new Error("Erro de leitura de dados"); 
		}
		
		if(!headerComplete) {
			
			var httpStatus:String;
			var headerEndIndex:int = str.indexOf("\r\n\r\n");
			if(headerEndIndex != -1) {
				//valor do header, a sobra sera tratada como data
				headerTmp += str.substring(0, headerEndIndex);
				data += str.substring(headerEndIndex + 4);
				
				//finalmente: trabalha os headers
				httpHeadersString = headerTmp; //armazena headers
				var headerArr:Array = headerTmp.split("\r\n"); //lista de headers
				var headerLine:String;
				var headerParts:Array;
				
				for each(headerLine in headerArr) {
					if(headerLine.indexOf("HTTP/1.") != -1) { //trabalha headers de evento/informação
						headerParts = headerLine.split(" ");
						if(headerParts.length > 0) {
							httpVersion = parseFloat(StringEx.trim(headerParts.shift().split("/")[1]));
							httpStatusCode = parseInt(StringEx.trim(headerParts.shift()));
							httpStatusText = StringEx.trim(headerParts.join(" "));
						}
						dispatchEvent(new HTTPStatusEvent(HTTPStatusEvent.HTTP_STATUS, false, false, httpStatusCode));
					} else { //headers de informação adicional
						var colonIndex:int = headerLine.indexOf(":");
						if(colonIndex != -1) {
							var key:String = StringEx.trim(headerLine.substring(0, colonIndex));
							var value:String = StringEx.trim(headerLine.substring(colonIndex + 1));
							if (!header) {
								header = new Dictionary();
							}
							header[key] = value;
						}
						if(header["Content-Length"]) {
							// total bytes = content-length + header size (number of characters in header)
							// not working, need to work on right logic..
							bytesTotal = int(header["Content-Length"]) + headerTmp.length;
							//trace("BYTES total: " + bytesTotal );
						}
					}
				}
				headerTmp = null;
			}
			
		} else { //acumula header para ser trabalhado
			if (headerTmp!=null) headerTmp += str; else data += str;
		}
	}
	
	private function sendHeaders():void {
		var h:String = "";
		var rq:URLRequestHeader;
		
		//create an HTTP 1.0 Header Request
		//Mh+= request.method + " " + request.url + " HTTP/1.0\r\n";
		//Mh+= "Host:" + httpServer + "\r\n";
			
		if(request.data) {
			h += request.data;
		} else {
			for each (rq in request.requestHeaders)
				h += rq.name + ": " + rq.value + "\r\n";			
		}
		
		//envia h
		//set HTTP headers to socket buffer
		//Msocket.writeUTFBytes(h + "\r\n\r\n");
		socket.writeUTFBytes(h);
		//push the data to server
		socket.flush();
	}
}
	
}